﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Thor.Common;
using Thor.Domain;
using Thor.Domain.Configuration;

namespace Thor.WorkerRole
{
    public class WorkerRole : RoleEntryPoint
    {
        private static object _lockObject = new Object();

        private bool IsThorConfigured()
        {
            bool configured = true;

            if (!SettingsHelper.AreSettingsConfigured())
            {
                Trace.TraceInformation("Thor's settings are not configured.");
                configured = false;
            }

            if (!CalendarProviderHelper.IsCalendarProviderConfigured())
            {
                Trace.TraceInformation("Thor's calendar provider is not configured.");
                configured = false;
            }

            if (!CalendarHelper.AreCalendarsConfigured())
            {
                Trace.TraceInformation("Thor's calendars are not configured.");
                configured = false;
            }

            return configured;
        }

        public override void Run()
        {
            // This is a sample worker implementation. Replace with your logic.
            Trace.TraceInformation("Thor.WorkerRole entry point called");
            
            if (IsThorConfigured())
            {
                Trace.TraceInformation("Starting calendar monitors.");
                CalendarMonitorHelper.StartMonitors();

                Trace.TraceInformation("Starting invalid logon monitor.");
                InvalidLogonMonitorHelper.StartMonitor();
            }
            else
                Trace.TraceInformation("Thor is configured.");

            while (true)
            {
                try
                {
                    lock (_lockObject)
                    {
                        var msg = MessageHelper.GetMessage();

                        if (msg != null)
                        {
                            Trace.TraceInformation(string.Format("Message received from queue: {0}", msg.Text));

                            if (IsThorConfigured())
                            {
                                if (CalendarMonitorHelper.IsRunning)
                                {
                                    Trace.TraceInformation("Restarting calendar monitors.");
                                    CalendarMonitorHelper.RestartMonitors();
                                }
                                else
                                {
                                    Trace.TraceInformation("Starting calendar monitors.");
                                    CalendarMonitorHelper.StartMonitors();
                                }

                                if (!InvalidLogonMonitorHelper.IsRunning)
                                {
                                    Trace.TraceInformation("Starting invalid logon monitor.");
                                    InvalidLogonMonitorHelper.StartMonitor();
                                }
                            }

                            MessageHelper.Clear();
                        }
                        else
                            Trace.TraceInformation("No messages in queue.");
                    }

                    System.Threading.Thread.Sleep(10000);
                }
                catch (Exception ex)
                {
                    System.Threading.Thread.Sleep(5000);
                    Trace.TraceError(string.Format("Exception encountered in Thor.WorkerRole.Run: '{0}'", ex.Message));
                }
            }

        }

        public override bool OnStart()
        {
            try
            {
                // Set the maximum number of concurrent connections 
                ServicePointManager.DefaultConnectionLimit = 12;

                Microsoft.WindowsAzure.Diagnostics.CrashDumps.EnableCollection(true);

                DiagnosticMonitorConfiguration diagConfig = DiagnosticMonitor.GetDefaultInitialConfiguration();

                diagConfig.Logs.ScheduledTransferLogLevelFilter = LogLevel.Information;
                diagConfig.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

                diagConfig.DiagnosticInfrastructureLogs.ScheduledTransferLogLevelFilter = LogLevel.Error;
                diagConfig.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

                diagConfig.WindowsEventLog.DataSources.Add("System!*");
                diagConfig.WindowsEventLog.DataSources.Add("Application!*");
                diagConfig.WindowsEventLog.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

                DiagnosticMonitor.Start("DiagnosticsConnectionString", diagConfig);

                #region Setup CloudStorageAccount Configuration Setting Publisher

                // This code sets up a handler to update CloudStorageAccount instances when their corresponding
                // configuration settings change in the service configuration file.
                CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
                {
                    // Provide the configSetter with the initial value
                    configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));

                    RoleEnvironment.Changed += (sender, arg) =>
                    {
                        if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
                            .Any((change) => (change.ConfigurationSettingName == configName)))
                        {
                            // The corresponding configuration setting has changed, propagate the value
                            if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
                            {
                                // In this case, the change to the storage account credentials in the
                                // service configuration is significant enough that the role needs to be
                                // recycled in order to use the latest settings. (for example, the 
                                // endpoint has changed)
                                RoleEnvironment.RequestRecycle();
                            }
                        }
                    };
                });

                #endregion

                return base.OnStart();
            }
            catch (Exception ex)
            {
                Trace.TraceError(string.Format("Exception encountered in Thor.WorkerRole.OnStart: '{0}'", ex.Message));
            }

            return false;
        }

        public override void OnStop()
        {
            try
            {
                if (CalendarMonitorHelper.IsRunning)
                    CalendarMonitorHelper.StopMonitors();

                if (InvalidLogonMonitorHelper.IsRunning)
                    InvalidLogonMonitorHelper.StopMonitor();

                base.OnStop();
            }
            catch (Exception ex)
            {
                Trace.TraceError(string.Format("Exception encountered in Thor.WorkerRole.OnStop: '{0}'", ex.Message));
            }
        }

        private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
        {
            // If a configuration setting is changing
            if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
            {
                // Set e.Cancel to true to restart this role instance
                e.Cancel = true;
            }
        }
    }
}
